好我覺得前面真的是在水,你們可以看看 Remix Tutorial,如果熟悉 React 跟 SSR 應該 30 分鐘真的可以完成,我的話當時因為真的不太熟,是看了四個小時才看完。
Ref: Remix Loader
這個是 Remix 伺服器端兩大頭 loader、action 之一,在 build 時會自動分類到 Server 端,所以這裡面執行的所有 function 在前端都是無法調用的,主要是處理 cookie、session 與資料取得,然後在前端的程式碼以 useLoaderData()
取用,下面來展示一下。
新的 React router 其實就是 remix 製作的東東是一樣的,這部分會寫在 route 資料夾裡頭,也可以自己整理在 *.server.ts
。Loader 會在 GET 時執行,然後 return 伺服器的 response。
資料流程是:
getUserSession
{ data: { ...user } }
我有發現如果在 return json({ })
裡面包含 toJSON() 的話會造成 useLoaderData<typeof loader>()
無法正確取得 type(user 會是 unknown)要避免,所以我 return 時解構 { data: { ...user } }
,這樣會自動篩掉 toJSON()。
*記得如果是寫在 loader 外面的資料,都會被曝在前端哦!
// file: /app/router/profile.tsx
// 我寫的 getUserSession 會從 request header 取得 cookie 之後解析 session,然後向 Firebase 取得 decodedIdToken。
import type { LoaderFunctionArgs } from '@remix-run/node'
import { json, redirect, useLoaderData } from '@remix-run/react'
import { getUserSession } from '~/data/session.server'
import { firebase } from '~/data/_firebase.server'
// loader 除了 request 之外也可以取得 route params 請見上篇
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
const userSession = await getUserSession(request)
if (!userSession) {
return redirect('/signin')
}
if (!userSession.decodedIdToken.email_verified) {
return redirect('/verify')
}
const user = await firebase.auth().getUser(userSession.decodedIdToken.uid)
// 用 remix 提供的 json 返回資料,可以在 json 後面用 <> 自定義你的 type
return json({
data: { ...user },
})
在同一個文件中,以 export default function()
寫 React。
// file: /app/router/profile.tsx
export default function Account() {
// 用 typeof loader remix 會自己 refer type
const { data: user } = useLoaderData<typeof loader>()
return (
// reder 你的 user
)
}
就這樣你就輕鬆做了一個前後端的 app 了!
因為 server 會把資料轉換成 JSON 送給前端,所以前端 type 如下,是 JsonifyObject:
這會是個大問題,如果把他直接傳給要求 Date 的 component,例如 createdAt: Date
就會出現:
這時候你就會想,啊 type 就是寫 createdAt: Date
為什麼不行?問題就是出在 JsonifyObject 會把 Date 轉換成 string,所以其實在前端取得的 JsonifyObject<createdAt: Date>
其實是 createdAt: string
。
要取得這個轉換後的 type,可以使用type SerializedLoaderDate = SerializeFrom<typeof loader>
這樣 SerializedLoaderData 就是你 useLoaderData 給前端的 type,也不需要自己定義 component 要接收什麼,一切 type 都依據 server 給的資料定義。